#include "OgreGLESFramebufferObject.h"
#include "OgreRoot.h"
#include "OgreGLESHardwarePixelBuffer.h"
#include "OgreGLESFBORenderTexture.h"

namespace Ogre
{
	GLESFrameBufferObject::GLESFrameBufferObject(GLESFBOManager* manager, uint fsaa) :
		mManager(manager), mNumSamples(fsaa)
	{
		glGenFramebuffersOES(1, &mFB);
		GL_CHECK_ERROR;
		
		mNumSamples = 0;
		mMultisampleFB = 0;
		
		mDepth.buffer = 0;
		mStencil.buffer = 0;
		for (size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x)
		{
			mColour[x].buffer = 0;
		}
	}
	
	GLESFrameBufferObject::~GLESFrameBufferObject()
	{
		mManager->releaseRenderBuffer(mDepth);
		mManager->releaseRenderBuffer(mStencil);
		mManager->releaseRenderBuffer(mMultisampleColourBuffer);
		glDeleteFramebuffersOES(1, &mFB);
		GL_CHECK_ERROR;
		
		if (mMultisampleFB)
			glDeleteFramebuffersOES(1, &mMultisampleFB);
		
		GL_CHECK_ERROR;
	}
	
	void GLESFrameBufferObject::bindSurface(size_t attachment, const GLESSurfaceDesc &target)
	{
		assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
		mColour[attachment] = target;
		if (mColour[0].buffer)
			initialise();
	}

	void GLESFrameBufferObject::unbindSurface(size_t attachment)
	{
		assert(attachment < OGRE_MAX_MULTIPLE_RENDER_TARGETS);
		mColour[attachment].buffer = 0;
		if (mColour[0].buffer)
			initialise();
	}

	void GLESFrameBufferObject::initialise()
	{
		mManager->releaseRenderBuffer(mDepth);
		mManager->releaseRenderBuffer(mStencil);
		mManager->releaseRenderBuffer(mMultisampleColourBuffer);
		if (!mColour[0].buffer)
		{
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"Attachment 0 must have surface attached",
				"GLESFrameBufferObject::initialise");
		}

		size_t width = mColour[0].buffer->getWidth();
		size_t height = mColour[0].buffer->getHeight();
		GLuint format = mColour[0].buffer->getGLFormat();
		PixelFormat ogreFormat = mColour[0].buffer->getFormat();

		glBindFramebufferOES(GL_FRAMEBUFFER_OES, mFB);
		GL_CHECK_ERROR;

		for (size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x)
		{
			if (mColour[x].buffer)
			{
				if (mColour[x].buffer->getWidth() != width || mColour[x].buffer->getHeight() != height)
				{
					StringStream ss;
					ss << "Attachment " << x << " has incompatible size ";
					ss << mColour[x].buffer->getWidth() << "x" << mColour[x].buffer->getHeight();
					ss << ". It must be of the same as the size of surface 0, ";
					ss << width << "x" << height;
					ss << ".";
					OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLESFrameBufferObject::initialise");
				}
				if (mColour[x].buffer->getGLFormat() != format)
				{
					StringStream ss;
					ss << "Attachment " << x << " has incompatible format.";
					OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, ss.str(), "GLESFrameBufferObject::initialise");
				}
				mColour[x].buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0_OES + x, mColour[x].zoffset);
			}
			else
			{
				glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES + x,
					GL_RENDERBUFFER_OES, 0);
				GL_CHECK_ERROR;
			}
		}

		if (mMultisampleFB)
		{
			glBindFramebufferOES(GL_FRAMEBUFFER_OES, mMultisampleFB);
			GL_CHECK_ERROR;

			mMultisampleColourBuffer = mManager->requestRenderBuffer(format, width, height, mNumSamples);

			mMultisampleColourBuffer.buffer->bindToFramebuffer(GL_COLOR_ATTACHMENT0_OES,
				mMultsampleColourBuffer.zoffset);
		}

		GLenum depthFormat, stencilFormat;
		mManager->getBestDepthStencil(ogreFormat, &depthFormat, &stencilFormat);

		mDepth = mManager->requestRenderBuffer(depthFormat, width, height, mNumSamples);
		if (depthFormat == GL_DEPTH24_STENCIL8_OES)
		{
			mManager->requestRenderBuffer(mDepth);
			mStencil = mDepth;
		}
		else
		{
			mStencil = mManager->requestRenderBuffer(stencilFormat, width, height, mNumSamples);
		}

		if (mDepth.buffer)
		{
			mDepth.buffer->bindToFramebuffer(GL_DEPTH_ATTACHMENT_OES, mDepth.zoffset);
		}
		else
		{
			glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES,
				GL_RENDERBUFFER_OES, 0);
			GL_CHECK_ERROR;
		}
		if (mStencil.buffer)
		{
			mStencil.buffer->bindToFramebuffer(GL_STENCIL_ATTACHMENT_OES, mStencil.zoffset);
		}
		else
		{
			glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES,
				GL_RENDERBUFFER_OES, 0);
			GL_CHECK_ERROR;
		}

		GLenum bufs[OGRE_MAX_MULTIPLE_RENDER_TARGETS];
		GLsizei n = 0;
		for (size_t x = 0; x < OGRE_MAX_MULTIPLE_RENDER_TARGETS; ++x)
		{
			if (mColour[x].buffer)
			{
				bufs[x] = GL_COLOR_ATTACHMENT_OES + x;
				n = x + 1;
			}
			else
			{
				bufs[x] = GL_NONE;
			}
		}

		GLuint status;
		status = glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES);
		GL_CHECK_ERROR;

#if OGRE_PLATFORM == OGRE_PLATFORM_IPHONE
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, 1);
#else
		glBindFramebufferOES(GL_FRAMEBUFFER_OES, 0);
#endif
		GL_CHECK_ERROR;

		switch(status)
		{
		case GL_FRAMEBUFFER_COMPLETE_OES:
			break;
		case GL_FRAMEBUFFER_UNSUPPORTED_OES:
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"All framebuffer formats with this texture internal format unsupported",
				"GLESFrameBufferObject::initialise");
		default:
			OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
				"Framebuffer incomplete or other FBO status error",
				"GLESFrameBufferObject::initialise");
		}
	}

	void GLESFrameBufferObject::bind()
	{
		if (mMultisampleFB)
			glBindFramebufferOES(GL_FRAMEBUFFER_OES, mMultisampleFB);
		else
			glBindFramebufferOES(GL_FRAMEBUFFER_OES, mFB);
		GL_CHECK_ERROR;
	}

	void GLESFrameBufferObject::swapBuffers()
	{

	}

	size_t GLESFrameBufferObject::getWidth()
	{
		assert(mColour[0].buffer);
		return mColour[0].buffer->getWidth();
	}

	size_t GLESFrameBufferObject::getHeight()
	{
		assert(mColour[0].buffer);
		return mColour[0].buffer->getHeight();
	}

	PixelFormat GLESFrameBufferObject::getFormat()
	{
		assert(mColour[0].buffer);
		return mColour[0].buffer->getFormat();
	}
}

